package com.gjintel.auction.business.websocket;

import com.gjintel.auction.business.model.AuctionCompareBid;
import com.gjintel.auction.business.model.AuctionInfo;
import com.gjintel.auction.business.model.AuctionLog;
import com.gjintel.auction.business.service.IAuctionCompareBidService;
import com.gjintel.auction.business.service.IAuctionLogService;
import com.gjintel.auction.business.service.IAuctionService;
import com.gjintel.auction.business.service.IEntryService;
import com.gjintel.auction.business.websocket.model.WebSocketInfo;
import com.gjintel.auction.business.websocket.model.WebSocketMessageBase;
import com.gjintel.auction.business.websocket.model.WebSocketReceiveMessage;
import com.gjintel.auction.business.websocket.model.WebSocketSendMessage;
import com.gjintel.framework.common.service.IDepositService;
import com.gjintel.framework.constant.Constant;
import com.gjintel.framework.dataset.service.IDataSetService;
import com.gjintel.framework.register.model.RegisterUser;
import com.gjintel.framework.user.model.User;
import com.gjintel.sdk.util.SpringContextUtil;
import com.gjintel.workflow.trade.service.IWorkflowProcessInstanceService;
import net.sf.json.JSONObject;
import org.activiti.engine.TaskService;
import org.activiti.engine.task.Task;


import javax.lang.model.type.ErrorType;
import javax.servlet.http.HttpSession;
import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * Created by Administrator on 2017/11/20.
 */
@ServerEndpoint(value = "/AuctionWebSocket/{sessionId}/{code}")
public class AuctionWebSocket {

    private static Map<String, Map<String, WebSocketInfo>> webSocketMap = new HashMap();

    private IAuctionLogService auctionLogService = (IAuctionLogService) SpringContextUtil.getBean("auctionLogService");

    private IAuctionCompareBidService auctionCompareBidService = (IAuctionCompareBidService) SpringContextUtil.getBean("auctionCompareBidService");

    private IAuctionService auctionService = (IAuctionService) SpringContextUtil.getBean("auctionService");

    private IEntryService entryService = (IEntryService) SpringContextUtil.getBean("entryService");

    private TaskService taskService = (TaskService) SpringContextUtil.getBean("taskService");

    private IDataSetService dataSetService = (IDataSetService) SpringContextUtil.getBean("dataSetService");

    private IWorkflowProcessInstanceService workflowProcessInstanceService = (IWorkflowProcessInstanceService) SpringContextUtil.getBean("workflowProcessInstanceService");

    private IDepositService depositService = (IDepositService) SpringContextUtil.getBean("depositService");

    //添加SocketSession
    private synchronized void AddSocketSession(User user, String code, boolean isStop, boolean isEntryAble, int depositStatus, HttpSession httpSession, Session session) {
        WebSocketInfo webSocketInfo = new WebSocketInfo(user, code, isStop, isEntryAble, depositStatus, httpSession, session);
        Map map = webSocketMap.get(code);
        if (map == null) {
            map = new HashMap();
        }
        map.put(session.getId(), webSocketInfo);
        this.webSocketMap.put(code, map);
    }

    //删除SocketSession
    private synchronized void DelSocketSession(String code, Session session) {
        try {
            Map maps = this.webSocketMap.get(code);
            maps.remove(session.getId());
            if (maps.size() <= 0) {
                this.webSocketMap.remove(code);
            }
        } catch (Exception ex) {
            //ex.printStackTrace();
        }
    }

    //获取当前WebSocketInfo
    private synchronized WebSocketInfo getWebSocketInfo(String code, Session session) {
        try {
            Map<String, WebSocketInfo> map = webSocketMap.get(code);
            WebSocketInfo webSocketInfo = map.get(session.getId());
            return webSocketInfo;
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return null;
    }

    private String getBaseSocketMessageJsonStr(String type, int errorCode, String message) {
        return JSONObject.fromObject(new WebSocketMessageBase(type, errorCode, message)).toString();
    }

    //校验保证金 保证金为0，不需要缴纳//已交为1,未交为0,错误  为 -1
    private int checkDeposit(RegisterUser registerUser, AuctionInfo auctionInfo) {
        //判断是否需要缴纳保证金
        if (auctionInfo.getDeposit() > 0) {
            // 判断是否缴纳单项保证金
            int ifPaySingleDeposit = depositService.ifPaySingleDeposit(registerUser.getId(), auctionInfo.getId(), registerUser.getLoginName(), auctionInfo.getAuctionName());
            return ifPaySingleDeposit;
        }
        return 1;
    }

    /**
     * 连接建立成功调用的方法
     *
     * @param session 可选的参数。session为与某个客户端的连接会话，需要通过它来给客户端发送数据
     */
    @OnOpen
    public synchronized void onOpen(@PathParam("sessionId") String sessionId, @PathParam("code") String code, Session session) throws Exception {
        MySessionContext myc = MySessionContext.getInstance();
        String message = "";
        try {
            do {
                boolean isEntryAble = true;//是否通过报名
                boolean isStop = false;//是否处于禁拍阶段
                int depositStatus = -1;//保证金状态
                HttpSession httpSession = myc.getSession(sessionId);
                if (httpSession == null) {
                    message = getBaseSocketMessageJsonStr("error", -1, "无法获取登录状态,请重新登录!");
                    break;
                }
                User user = (User) httpSession.getAttribute("loginUser");
                if (user == null) {
                    message = getBaseSocketMessageJsonStr("error", -1, "获取用户登录信息失败,请重新登录!");
                    break;
                }
                AuctionInfo auctionInfo = auctionService.findByCode(code);
                if (auctionInfo == null) {
                    message = getBaseSocketMessageJsonStr("error", -2, "查询项目失败!");
                    break;
                }
                RegisterUser registerUser = this.auctionService.get(RegisterUser.class, user.getRegisterId());
                if (registerUser == null) {
                    message = getBaseSocketMessageJsonStr("error", -1, "获取用户注册信息失败,请重新登录!");
                    break;
                }
                Date dateNow = new Date();
                Date forbidEndDate = registerUser.getForbidEndDate(); //禁投结束时间
                //判断用户是否处于禁止拍卖
                if (forbidEndDate != null && dateNow.getTime() < forbidEndDate.getTime()) {
                    //禁止拍卖
                    isStop = true;
                    sendMsg(session, getBaseSocketMessageJsonStr("stop", 0, "您当前用户状态是禁拍状态,不能参与竞拍!"), false);
                    this.AddSocketSession(user, code, isStop, isEntryAble, depositStatus, httpSession, session);
                    return;
                }
                //封存用户和删除用户不能正常竞拍
                if (user.getFlag() != 0) {
                    sendMsg(session, getBaseSocketMessageJsonStr("userFlag", user.getFlag(), "您当前用户状态是封存状态,不能参与竞拍!"), false);
                    this.AddSocketSession(user, code, isStop, isEntryAble, depositStatus, httpSession, session);
                    return;
                }
                //判断是否需要报名
                if (auctionInfo.getEntryAble() == true && entryService.getEntryAble(auctionInfo.getId(), user.getId()) == false) {
                    isEntryAble = false;
                    //没有通过报名
                    sendMsg(session, getBaseSocketMessageJsonStr("entryAble", 0, "您当前没有通过报名,不能参与竞拍!"), false);
                    this.AddSocketSession(user, code, isStop, isEntryAble, depositStatus, httpSession, session);
                    return;
                }
                //获取保证金状态
                depositStatus = checkDeposit(registerUser, auctionInfo);
                //是否交了保证金
                if (depositStatus != 1) {
                    //没有交保证金
                    sendMsg(session, getBaseSocketMessageJsonStr("deposit", depositStatus, "您当前保证金校验失败,不能参与竞拍!"), false);
                    this.AddSocketSession(user, code, isStop, isEntryAble, depositStatus, httpSession, session);
                    return;
                }
                long time = System.currentTimeMillis();
                if (time < auctionInfo.getAuctionStart().getTime()) {
                    message = getBaseSocketMessageJsonStr("error", 0, "竞拍尚未开始!");
                    break;
                }
                if (time > auctionInfo.getAuctionEnd().getTime()) {
                    message = getBaseSocketMessageJsonStr("error", 0, "竞拍已经结束!");
                    break;
                }
                Task task = workflowProcessInstanceService.findTask(auctionInfo.getWorkflowId());
                if (task == null) {
                    message = getBaseSocketMessageJsonStr("error", -2, "无法获取当前流程!");
                    break;
                }
                if (task.getName().equals("竞拍") == false && task.getName().equals("修改竞拍时间") == false) {
                    message = getBaseSocketMessageJsonStr("error", -2, "当前流程,不是竞拍流程");
                    break;
                }
                this.AddSocketSession(user, code, isStop, isEntryAble, depositStatus, httpSession, session);
                sendMsg(session, getBaseSocketMessageJsonStr("OK", depositStatus, "连接服务器成功!"), false);
                System.out.println("有新连接加入！当前用户为:" + user.getLoginName());
                return;
            } while (false);
        } catch (Exception ex) {
            ex.printStackTrace();
            message = getBaseSocketMessageJsonStr("error", -2, ex.getMessage());
        }
        //发送给登录者并断开链接
        sendMsg(session, message, true);
    }

    /**
     * 连接关闭调用的方法
     */
    @OnClose
    public synchronized void onClose(@PathParam("sessionId") String sessionId, @PathParam("code") String code, Session session, CloseReason reason) {
        this.DelSocketSession(code, session);
        System.out.println("有一连接关闭！ " + sessionId);
    }

    /**
     * 收到客户端消息后调用的方法
     *
     * @param message 客户端发送过来的消息
     * @param session 可选的参数
     */
    @OnMessage
    public synchronized void onMessage(@PathParam("sessionId") String sessionId, @PathParam("code") String code, String message, Session session) {
        try {
            System.out.println("来自客户端的消息:" + message);
            JSONObject jsonObject = new JSONObject().fromObject(message);//将json字符串转换为json对象
            //将json对象转换为java对象
            WebSocketReceiveMessage webSocketReceiveMessage = (WebSocketReceiveMessage) JSONObject.toBean(jsonObject, WebSocketReceiveMessage.class);//将建json对象转换为Person对象
            if (webSocketReceiveMessage == null || webSocketReceiveMessage.getType().equals("bid") == false) {
                throw new Exception(getBaseSocketMessageJsonStr("error", -2, "参数错误!"));
            }
            AuctionInfo auctionInfo = auctionService.findByCode(code);
            if (auctionInfo == null) {
                throw new Exception(getBaseSocketMessageJsonStr("error", -2, "查询项目失败!"));
            }
            long time = System.currentTimeMillis();
            if (time < auctionInfo.getAuctionStart().getTime()) {
                throw new Exception(getBaseSocketMessageJsonStr("error", -3, "竞拍尚未开始!"));
            }
            if (time > auctionInfo.getAuctionEnd().getTime()) {
                throw new Exception(getBaseSocketMessageJsonStr("error", -3, "竞拍已经结束!"));
            }
            WebSocketInfo webSocketInfo = getWebSocketInfo(code, session);
            if (webSocketInfo == null) {
                throw new Exception(getBaseSocketMessageJsonStr("error", -2, "获取用户登录信息失败,请重新登录!"));
            }
            User user = webSocketInfo.getUser();
            //封存用户和删除用户不能正常竞拍
            if (user.getFlag() != 0) {
                sendMsg(session, getBaseSocketMessageJsonStr("userFlag", user.getFlag(), "您当前用户状态是封存状态,不能参与竞拍!"), false);
                return;
            }
            //是否处于禁拍阶段
            if (webSocketInfo.isStop() == true) {
                //竞拍状态
                sendMsg(session, getBaseSocketMessageJsonStr("stop", 0, "您当前用户状态是禁拍状态,不能参与竞拍!"), false);
                return;
            }
            //是否通过报名
            if (webSocketInfo.isEntryAble() == false) {
                //没有通过报名
                sendMsg(session, getBaseSocketMessageJsonStr("entryAble", 0, "您当前没有通过报名,不能参与竞拍!"), false);
                return;
            }
            //是否交了保证金
            if (webSocketInfo.getDepositStatus() != 1) {
                //没有交保证金
                sendMsg(session, getBaseSocketMessageJsonStr("deposit", webSocketInfo.getDepositStatus(), "您当前保证金校验失败,不能参与竞拍!"), false);
                return;
            }
            AuctionLog auctionLog = new AuctionLog();
            auctionLog.setAuctionId(auctionInfo.getId());
            auctionLog.setBidder(user.getLoginName());
            auctionLog.setBidderId(user.getId());
            auctionLog.setBidPrice(webSocketReceiveMessage.getPrice());
            auctionLog.setBidTime(time);
            //添加竞拍日志
            auctionLogService.save(auctionLog);
            if (webSocketReceiveMessage.getPrice().compareTo(auctionInfo.GetNewPriceBig()) <= 0) {
                sendMsg(session, getBaseSocketMessageJsonStr("error", 0, "出价失败,该[" + webSocketReceiveMessage.getPrice() + "]价格不是最高价格!"), false);
                return;
            }
            Integer auction_sleep = dataSetService.getDataSet().getAuctionSleep();
            if (auction_sleep > 0) {
                //计算剩余时间是否小于3分钟
                if (auctionInfo.getAuctionEnd().getTime() - time < auction_sleep * 1000) {
                    Date dateEnd = new Date(time + auction_sleep * 1000);//通过当前时间,追加结束时间
                    auctionInfo.setAuctionEnd(dateEnd);
                    try {
                        Task task = taskService.createTaskQuery().processInstanceId(auctionInfo.getWorkflowId()).singleResult();
                        if (task.getName().equals("竞拍") == true) {
                            //重置流程结束时间
                            taskService.complete(task.getId());
                        }
                    } catch (Exception ex) {
                        ex.printStackTrace();
                    }
                }
            }

            //返回信息
            WebSocketSendMessage webSocketSendMessage = new WebSocketSendMessage();
            webSocketSendMessage.setType("bid");
            webSocketSendMessage.setMessage("竞拍成功");
            webSocketSendMessage.setErrorCode(0);
            webSocketSendMessage.setNowprice(webSocketReceiveMessage.getPrice().toString());
            webSocketSendMessage.setBiddrId(user.getId());
            webSocketSendMessage.setBidderName(user.getLoginName());
            webSocketSendMessage.setBidderTime(time);
            webSocketSendMessage.setEndtime(auctionInfo.getAuctionEnd().getTime());
            webSocketSendMessage.setCode(code);
            webSocketSendMessage.setAuctionId(auctionInfo.getId());
            String result = JSONObject.fromObject(webSocketSendMessage).toString();

            //报价信息
            AuctionCompareBid auctionCompareBid = new AuctionCompareBid();
            auctionCompareBid.setAuctionId(auctionInfo.getId());
            auctionCompareBid.setBidderName(user.getLoginName());
            auctionCompareBid.setBiddrId(user.getId());
            auctionCompareBid.setBidderPrice(webSocketReceiveMessage.getPrice());
            auctionCompareBid.setBidderTime(time);
            auctionCompareBid.setHitAble(false);
            auctionInfo.SetNewPriceBig(webSocketReceiveMessage.getPrice());


            auctionCompareBidService.save(auctionCompareBid);//添加报价信息
            auctionService.saveOrUpdate(auctionInfo);//更新最新价格
            sendAllMsg(code, result);//群发消息
        } catch (Exception ex) {
            sendMsg(session, ex.getMessage(), true);
        }
    }

    /**
     * 发生错误时调用
     *
     * @param session
     * @param error
     */
    @OnError
    public void onError(@PathParam("sessionId") String sessionId, @PathParam("code") String code, Session session, Throwable error) {
        this.DelSocketSession(code, session);
        System.out.println("发生错误");
        error.printStackTrace();
    }


    //发消息
    private void sendMsg(Session session, String msg, Boolean isDisconnect) {
        try {
            session.getBasicRemote().sendText(msg);
            if (isDisconnect) {
                session.close();
            }
        } catch (IOException ex) {
            ex.printStackTrace();
        }
    }

    //根据项目ID群发消息
    private synchronized void sendAllMsg(String code, String msg) {
        Map<String, WebSocketInfo> map = webSocketMap.get(code);
        for (WebSocketInfo webSocketInfo : map.values()) {
            try {
                webSocketInfo.getSession().getBasicRemote().sendText(msg);
            } catch (IOException ex) {
                ex.printStackTrace();
            }
        }
    }
}
